
function UI(client, onerror_cb)
{
    this.client = client;
    this.games = {};
    this.currentGameId = undefined;
    this.views = [];
    this.state = new StateMachine();
    this.pollrequest = null;
    this.pollinterval = -1;
    this.onerror_callback = onerror_cb;
    
    this.changeGame = function(gameid)
    {
        this.state.reset();
        this.currentGameId = gameid;
        this.updateAllViews();
    }
    
    this.addGame = function(gameid)
    {
        if(this.games[gameid] != undefined) return;
        this.games[gameid] = new Game(gameid, ['',''], undefined);
    }
    
    this.removeGame = function(gameid){
        var newgames = {}
        var newcurrentgameid = undefined;
        for(id in this.games){
            if(id != gameid){
                newgames[id] = this.games[id];
                newcurrentgameid = id;
            }
        }
        this.games = newgames;
        this.currentGameId = newcurrentgameid;
        this.updateAllViews();
    }
    
    this.start = function()
    {
        var interval = 2000;
        this.poll(interval); // TODO: configurable poll interval
        this.updateAllViews();
    }
    
    this.stop = function()
    {
        this.pollinterval = -1;
    }
    
    this.poll = function(interval)
    {
        this.pollinterval = interval;
    
        gamelist = []
        for(var gameid in this.games)
        {
            var turn = (this.games[gameid] != null) ? this.games[gameid].currentTurn() : 0;
            gamelist.push([gameid, turn]);
        }
        
        var ui = this;
        function onturns(reply)
        {
            var updated = false;
			for(var i in reply.games)
            {
				var game = reply.games[i];
				var upd = ui.updateGame(game.gameid, game.players, game.info, game.turn, game.turns);
				if(upd && ui.currentGameId == game.gameid) ui.updateCurrentGameViews();
				updated |= upd;
            }
        
            if(updated) ui.updateGameListViews();
        
            if(ui.pollinterval >= 0)
            {
                setTimeout(function() { ui.poll(ui.pollinterval); }, ui.pollinterval)
            }
            
        }
        
        function onerror(err)
        {
            ui.onerrorWrapper("Getturns failed: " + err);
        }
        
        this.pollrequest = client.command('getturns', gamelist, onturns, onerror);
    }
    
    this.updateGame = function(gameid, players, info, turn, turnlist)
    {
        if(this.games[gameid] == undefined) throw "invalid game id in updateGame" + gameid + " " + JSON.stringify(this.games);
        var game= this.games[gameid];
        if(game.currentTurn() < turn - turnlist.length) throw "invalid turn in updateGame";
        if(turn < game.currentTurn()) // newer states already recieved
            return;
        
        var init = game.initialized();
        game.players = players;
        game.info = info;
        
        var first = Math.max(game.currentTurn(), turn - turnlist.length);
        var count = turn - first;

        for(var i = turnlist.length-count; i < turnlist.length; ++i)
        {
            var head = turnlist[i];
            hierarkia = new Hierarkia(info, head.cells, head.lastMove);
            game.addTurn(hierarkia);
        }

        if((game.initialized() ^ init) || count >= 1)
        {
            if(this.currentGameId && gameid == this.currentGameId)
                this.state.reset();
            
            return true;
        }
        
        return false;
    }

	var ui = this;
	function sendmove(game, newhierarkia)
	{
		function onmovereply(reply)
		{
			if(!reply.accepted) return;
			if(ui.updateGame(
				reply.game.gameid,
				reply.game.players,
				reply.game.info,
				reply.game.turn,
				reply.game.turns))
				ui.updateAllViews();
		};
		
		function onerror(err)
		{
			ui.onerrorWrapper('Turn request failed: ' + err);
		}

		ui.client.command(
			'turn',
			{'turn': game.currentTurn()+1, 
			'gameid': game.id,
			'newstate':  newhierarkia},
			onmovereply,
			onerror
			);
	}

    this.cellClicked = function(x, y)
    {
        if(!this.currentGameId) return;
        if(!this.client.auth) return;
        var game = this.games[this.currentGameId];
        if(!game.initialized()) return;
        
        var player = this.client.username;
        
        if(this.state.changeState(game, player, x, y))
        {
            if(this.state.state == UIState.MOVE || this.state.state == UIState.ATTACK)
            {
				var newhierarkia =
					(this.state.state == UIState.MOVE)
					? game.hierarkia().makeMove(this.state.from, this.state.to)
					: game.hierarkia().makeAttack(this.state.from, this.state.to);   

				sendmove(game, newhierarkia);
            }

            
            this.updateAllViews();
        }
    }

	this.pass = function()
	{
        if(!this.currentGameId) return;
        if(!this.client.auth) return;
        var game = this.games[this.currentGameId];
        if(!game.initialized()) return;
        var player = this.client.username;
		if(game.currentPlayer() != player) return;

		sendmove(game, game.hierarkia().pass()); 
	}
    
    this.updateAllViews = function()
    {
        this.updateGameListViews();
        this.updateCurrentGameViews();
    }
    
    this.updateGameListViews = function()
    {
        for(var i in this.views)
            this.views[i].updateGamesList(this.client.username, this.currentGameId, this.games);
    }
    
    this.updateCurrentGameViews = function()
    {
        if(!this.currentGameId) return;
        var game = this.games[this.currentGameId];
 
        if(!game.initialized()) return;
    
        for(var i in this.views)
            this.views[i].updateCurrentGame(this.client.username, game, this.state);
    }
    
    this.addView = function(view) { this.views.push(view); }
    
    var self = this;
    this.onerrorWrapper = function(err)
    {
        self.onerror_callback(err);
    }
    
    this.onlogin = function(username)
    {
        function addGames(reply){
            for(var i in reply.games){
                self.addGame(reply.games[i].gameid);
            }
        }
        this.client.command('getmygames', '', addGames, self.onerror_callback);

        this.updateAllViews();
    }

    this.onlogout = function()
    {
        this.games = {};
        this.updateAllViews();
    }
}


